package com.ruiyou.ub;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.DownloadManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.webkit.CookieManager;
import android.webkit.WebBackForwardList;
import android.webkit.WebSettings;
import android.widget.Button;
import android.widget.EditText;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupMenu;
import android.widget.ProgressBar;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;

import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContract;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.splashscreen.SplashScreen;
import androidx.core.view.GravityCompat;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.core.view.WindowInsetsControllerCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.FragmentManager;
import androidx.preference.PreferenceManager;
import androidx.webkit.WebSettingsCompat;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewFeature;

import com.google.android.material.tabs.TabLayout;

import java.io.FileNotFoundException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private final int MAX_DELAY = 5000;
    private final int CODE_SCANNING = 0;
    private final int CODE_SCAN_LOCAL_PICTURE = 1;
    private final String FLAG_URL = "url";
    private final String FLAG_TITLE = "title";
    private final String EXTRA_REQUEST_CODE = "extra_launch_activity";
    private final String SHARED_PREF = "main.sp";
    private final String KEY_BOOKMARK = "bookmark:";
    private final String KEY_HISTORY = "history:";
    private final String KEY_HOME = "home:";
    private final HashMap<String, String> mBookmarks = new HashMap<>();
    private final List<Map<String, String>> mHistory = new ArrayList<>();
    private final List<Map<String, String>> mListData = new ArrayList<>();
    private long mLastBackPressedTime = 0;
    private boolean isRefreshed, isPortrait;
    private String currentFindText = "";
    private String mHomeUrl = "about:blank";
    private ActivityResultLauncher<Intent> mActivityLauncher;
    private Timer mTimer;
    private SimpleAdapter mListAdapter;
    // View
    private UWebView mWebView;
    private Button mButtonRefresh, mButtonJS, mButtonImage, mButtonFullscreen;
    private HorizontalScrollView mBottomControlPanel;
    private ProgressBar mProgressBar;
    private Toolbar mToolbar;
    private EditText mToolbarEdit;
    private Menu mOptionMenu;
    private LinearLayout layoutFind;
    private TextView tvFindState;
    private EditText etFindText;
    private DrawerLayout mDrawerLayout;
    private TabLayout mDrawerTab;
    private MovableFAB mFAB;
    private MenuItem mMenuRefresh, mMenuLoadImage, mMenuJavascript;
    private ListView mDrawerList;
    private final DrawerLayout.DrawerListener mDrawerListener
            = new DrawerLayout.DrawerListener() {
        @Override
        public void onDrawerOpened(@NonNull View drawerView) {
            updateBookmarkOrHistory(
                    mListData,
                    mDrawerTab.getSelectedTabPosition() == 0
            );
            mListAdapter.notifyDataSetChanged();
        }

        @Override
        public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
        }

        @Override
        public void onDrawerClosed(@NonNull View drawerView) {
        }

        @Override
        public void onDrawerStateChanged(int newState) {
        }
    };
    private final TabLayout.OnTabSelectedListener mOnTabSelectedListener
            = new TabLayout.OnTabSelectedListener() {
        @Override
        public void onTabSelected(TabLayout.Tab tab) {
            updateBookmarkOrHistory(
                    mListData,
                    tab.getPosition() == 0
            );
            mListAdapter.notifyDataSetChanged();
        }

        @Override
        public void onTabUnselected(TabLayout.Tab tab) {
        }

        @Override
        public void onTabReselected(TabLayout.Tab tab) {
        }
    };
    private final SharedPreferences.OnSharedPreferenceChangeListener mPreferenceListener
            = this::applySettings;

    private enum SettingType {ENABLED_JS, BLOCK_NETWORK_IMAGE}

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        SplashScreen.installSplashScreen(this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        takePermission();
        bindView();
        // load web page
        setSupportActionBar(findViewById(R.id.toolbar));
        if (getIntent().getData() != null) {
            // In case: load current activity from other app in running state.
            mWebView.loadUrl(getIntent().getData().toString());
        } else {
            mWebView.loadUrl(mHomeUrl);
        }
    }

    private void handleActivityResult(ActivityResult result) {
        Intent intent = result.getData();
        if (result.getResultCode() != RESULT_OK || intent == null) {
            return;
        }
        int requestCode = intent.getIntExtra(
                EXTRA_REQUEST_CODE, -1
        );
        Uri uri = intent.getData();
        if (requestCode == CODE_SCANNING) {
            String str = intent.getStringExtra(ScanningActivity.EXTRA_RESULT);
            Util.showDialog(
                    this,
                    getString(R.string.scanning_result),
                    str == null ? "null" : str,
                    DialogType.INFO,
                    null
            );
        } else if (requestCode == CODE_SCAN_LOCAL_PICTURE && uri != null) {
            String[] array = {null};
            Runnable runnable = () -> {
                try {
                    BitmapFactory.Options opts = new BitmapFactory.Options();
                    opts.inJustDecodeBounds = true;
                    BitmapFactory.decodeStream(
                            getContentResolver().openInputStream(uri),
                            null, opts
                    );
                    int outMax = Math.max(opts.outWidth, opts.outHeight);
                    if (outMax > 1280) {
                        opts.inSampleSize = outMax / 1280;
                    }
                    opts.inJustDecodeBounds = false;
                    Bitmap bmp = BitmapFactory.decodeStream(
                            getContentResolver().openInputStream(uri),
                            null,
                            opts
                    );
                    if (bmp != null) {
                        array[0] = Util.decodeQRCode(bmp);
                    }
                } catch (FileNotFoundException e) {
                    array[0] = e.getMessage();
                }
                runOnUiThread(() -> Util.showDialog(
                        MainActivity.this,
                        getString(R.string.scanning_result),
                        array[0] == null ? "null" : array[0],
                        DialogType.INFO,
                        null
                ));
            };
            new Thread(runnable).start();
        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        // While load current activity from other app on running state.
        String s = intent.getDataString();
        if (s != null) {
            mWebView.loadUrl(s);
        }
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        // On immersive mode hide system bars again.
        if (!isPortrait) {
            setSysUiVisibility();
        }
    }

    @Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        isPortrait = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT;
        changeConfiguration();
    }

    private void changeConfiguration() {
        inflateMenu();
        int imgRes = mToolbar.getVisibility() == View.VISIBLE ?
                R.drawable.ic_fullscreen_24 : R.drawable.ic_no_fullscreen_24;
        setSysUiVisibility();
        if (isPortrait) {
            mFAB.setImageResource(R.drawable.ic_arrow_left_24);
            mBottomControlPanel.setVisibility(View.VISIBLE);
            setBottomPanelVisibility(true, 0);
            mButtonFullscreen.setBackgroundResource(imgRes);
        } else {
            mFAB.setImageResource(imgRes);
            mBottomControlPanel.setVisibility(View.GONE);
        }
        updateSettingsState();
        mFAB.resizeParent();
    }

    private void setSysUiVisibility() {
        WindowInsetsControllerCompat controller = new WindowInsetsControllerCompat(
                getWindow(), getWindow().getDecorView()
        );
        if (isPortrait) {
            WindowCompat.setDecorFitsSystemWindows(getWindow(), true);
            controller.show(WindowInsetsCompat.Type.systemBars());
        } else {
            controller.hide(WindowInsetsCompat.Type.systemBars());
            WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
            controller.setSystemBarsBehavior(
                    WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
            );
        }
    }

    /**
     * 退出前保存数据
     */
    @Override
    protected void onStop() {
        saveDataToPref();
        super.onStop();
    }

    /**
     * 检查权限
     */
    private void takePermission() {
        String[] ps = {
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
        };
        for (String p : ps) {
            if (ContextCompat.checkSelfPermission(this, p) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this, ps, 3956);
                return;
            }
        }
    }

    /**
     * 初始化相关变量
     */
    private void bindView() {
        //bind view
        List<View> performClickViews = new ArrayList<>(Arrays.asList(
                findViewById(R.id.btn_find_prev),
                findViewById(R.id.btn_find_next),
                findViewById(R.id.btn_find_cancel),
                mFAB = findViewById(R.id.fab),
                findViewById(R.id.btn_home),
                findViewById(R.id.btn_back),
                findViewById(R.id.btn_forward),
                mButtonRefresh = findViewById(R.id.btn_refresh),
                mButtonJS = findViewById(R.id.btn_javascript),
                mButtonImage = findViewById(R.id.btn_image),
                mButtonFullscreen = findViewById(R.id.btn_fullscreen)
        ));
        for (View v : performClickViews) {
            v.setOnClickListener(this);
        }
        mBottomControlPanel = findViewById(R.id.bottom_control_panel);
        mDrawerLayout = findViewById(R.id.drawer_layout);
        mDrawerList = findViewById(R.id.list_bookmark_history);
        mDrawerTab = findViewById(R.id.tab_bookmark_and_history);
        mToolbar = findViewById(R.id.toolbar);
        mToolbarEdit = findViewById(R.id.toolbar_edit);
        layoutFind = findViewById(R.id.layout_find);
        tvFindState = findViewById(R.id.tv_find_state);
        etFindText = findViewById(R.id.et_find);
        mProgressBar = findViewById(R.id.pb);
        // *******
        mWebView = findViewById(R.id.web_view);
        initData();
    }

    @SuppressLint("ClickableViewAccessibility")
    private void initData() {
        if(WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)){
            WebSettingsCompat.setAlgorithmicDarkeningAllowed(mWebView.getSettings(),true);
        }
        readSettings();
        readDataFromPref();
        ActivityResultContract<Intent, ActivityResult> contract =
                new ActivityResultContract<Intent, ActivityResult>() {
                    int requestCode;

                    @NonNull
                    @Override
                    public Intent createIntent(@NonNull Context context, Intent input) {
                        requestCode = input.getIntExtra(EXTRA_REQUEST_CODE, -1);
                        return input;
                    }

                    @Override
                    public ActivityResult parseResult(int resultCode, @Nullable Intent intent) {
                        if (intent != null) {
                            intent.putExtra(EXTRA_REQUEST_CODE, requestCode);
                        }
                        return new ActivityResult(resultCode, intent);
                    }
                };
        mActivityLauncher = registerForActivityResult(
                contract,
                this::handleActivityResult
        );
        mListAdapter = new SimpleAdapter(this, mListData,
                R.layout.list_view_item, new String[]{FLAG_TITLE, FLAG_URL}
                , new int[]{R.id.list_item_title, R.id.list_item_content});
        mDrawerList.setAdapter(mListAdapter);
        isPortrait = getResources().getConfiguration().orientation
                == Configuration.ORIENTATION_PORTRAIT;
        changeConfiguration();
    }

    @Override
    protected void onResume() {
        super.onResume();
        addListeners();
    }

    @Override
    protected void onPause() {
        super.onPause();
        removeListeners();
    }

    private void addListeners() {
        mWebView.setFindListener((ordinal, numMatches, b) -> {
            ordinal = numMatches == 0 ? 0 : ordinal + 1;
            String s = ordinal + "/" + numMatches;
            tvFindState.setText(s);
        });
        mWebView.setStateListener((v, stateType, args) ->
                onWebViewStateChanged(stateType, args));
        mDrawerList.setOnItemClickListener(
                (parent, view, position, id) -> {
                    String url = mListData.get(position).get(FLAG_URL);
                    if (url != null) {
                        mWebView.loadUrl(url);
                        mDrawerLayout.closeDrawer(GravityCompat.START);
                    }
                });
        mDrawerList.setOnItemLongClickListener(
                (parent, view, position, id) -> {
                    final PopupMenu menu = new PopupMenu(this, view);
                    TextView textViewUrl = view.findViewById(R.id.list_item_content);
                    TextView textViewTitle = view.findViewById(R.id.list_item_title);
                    menu.getMenu().add(R.string.delete).setOnMenuItemClickListener(item -> {
                        if (mDrawerTab.getSelectedTabPosition() == 0) {
                            mBookmarks.remove(textViewUrl.getText().toString());
                        } else {
                            HashMap<String, String> map = new HashMap<>();
                            map.put(FLAG_URL, textViewUrl.getText().toString());
                            map.put(FLAG_TITLE, textViewTitle.getText().toString());
                            mHistory.remove(map);
                        }
                        mListData.remove(position);
                        mListAdapter.notifyDataSetChanged();
                        return true;
                    });
                    menu.getMenu().add(R.string.clear).setOnMenuItemClickListener(item -> {
                        Util.showDialog(
                                this,
                                getString(R.string.warning),
                                getString(R.string.confirm_clear),
                                DialogType.CONFIRM,
                                result -> {
                                    if (mDrawerTab.getSelectedTabPosition() == 0) {
                                        mBookmarks.clear();
                                    } else {
                                        mHistory.clear();
                                        mWebView.clearHistory();
                                    }
                                    mListData.clear();
                                    mListAdapter.notifyDataSetChanged();
                                }
                        );
                        return true;
                    });
                    menu.getMenu().add(R.string.detail).setOnMenuItemClickListener(
                            item -> {
                                Util.showDialog(
                                        this,
                                        textViewTitle.getText().toString(),
                                        textViewUrl.getText().toString(),
                                        DialogType.INFO,
                                        null

                                );
                                return true;
                            });
                    menu.show();
                    return true;
                });
        mDrawerLayout.addDrawerListener(mDrawerListener);
        mDrawerTab.addOnTabSelectedListener(mOnTabSelectedListener);
        PreferenceManager.getDefaultSharedPreferences(this)
                .registerOnSharedPreferenceChangeListener(mPreferenceListener);
    }

    private void removeListeners() {
        mDrawerLayout.removeDrawerListener(mDrawerListener);
        mDrawerTab.removeOnTabSelectedListener(mOnTabSelectedListener);
        PreferenceManager.getDefaultSharedPreferences(this)
                .unregisterOnSharedPreferenceChangeListener(mPreferenceListener);
    }

    private void onWebViewStateChanged(int stateType, Object[] args) {
        switch (stateType) {
            case UWebView.StateListener.STATE_STARTED:
                isRefreshed = false;
                mToolbar.post(() -> {
                    if (isPortrait) {
                        mButtonRefresh.setBackgroundResource(R.drawable.ic_cancel);
                    } else {
                        mMenuRefresh.setIcon(R.drawable.ic_cancel);
                    }
                });
                mProgressBar.setVisibility(View.VISIBLE);
                mToolbar.setTitle((String) args[0]);
                break;
            case UWebView.StateListener.STATE_FINISHED:
                isRefreshed = true;
                mToolbar.post(() -> {
                    if (isPortrait) {
                        mButtonRefresh.setBackgroundResource(R.drawable.ic_refresh);
                    } else {
                        mMenuRefresh.setIcon(R.drawable.ic_refresh);
                    }
                });
                mProgressBar.setVisibility(View.GONE);
                break;
            case UWebView.StateListener.STATE_PROGRESS:
                mProgressBar.setProgress((int) args[0]);
                break;
            case UWebView.StateListener.STATE_FIND_START:
                currentFindText = args[0].toString();
                mWebView.findAllAsync(currentFindText);
                layoutFind.setVisibility(View.VISIBLE);
                etFindText.setText(currentFindText);
                break;
            case UWebView.StateListener.STATE_RECEIVED_TITLE:
                mToolbar.setTitle(args[0].toString());
                break;
        }
    }

    /**
     * 保存设置
     */
    private void saveDataToPref() {
        SharedPreferences.Editor editor =
                getSharedPreferences(SHARED_PREF, 0).edit();
        // Reset
        editor.clear();
        // Save bookmarks
        StringBuilder sb = new StringBuilder();
        if (!mBookmarks.isEmpty()) {
            for (Map.Entry<String, String> entry : mBookmarks.entrySet()) {
                sb.append(entry.getKey())
                        .append("\n")
                        .append(entry.getValue())
                        .append("\n");
            }
            editor.putString(KEY_BOOKMARK, sb.toString().trim());
        }
        // Save History
        List<Map<String, String>> historyData = new ArrayList<>();
        updateBookmarkOrHistory(historyData, false);
        // Change the order as the web view history list. The index 0 is the newer and
        // the list data index 0 populated at the bottom.
        if (!historyData.isEmpty()) {
            sb.delete(0, sb.length());
            for (Map<String, String> map : historyData) {
                sb.append(map.get(FLAG_URL))
                        .append("\n")
                        .append(map.get(FLAG_TITLE))
                        .append("\n");
            }
            editor.putString(KEY_HISTORY, sb.toString().trim());
        }
        // Save homeUrl
        editor.putString(KEY_HOME, mHomeUrl);
        editor.apply();
    }

    /**
     * 读取设置
     */
    private void readDataFromPref() {
        SharedPreferences prefs = getSharedPreferences(SHARED_PREF, 0);
        // Bookmarks
        String str = prefs.getString(KEY_BOOKMARK, null);
        if (str != null) {
            String[] array = str.split("\n");
            for (int i = 0; i < array.length - 1; i += 2) {
                mBookmarks.put(array[i], array[i + 1]);
            }
        }
        // History
        if (!mHistory.isEmpty()) mHistory.clear();
        str = prefs.getString(KEY_HISTORY, null);
        if (str != null) {
            String[] array = str.split("\n");
            for (int i = 0; i < array.length - 1; i += 2) {
                Map<String, String> map = new HashMap<>();
                map.put(FLAG_URL, array[i]);
                map.put(FLAG_TITLE, array[i + 1]);
                mHistory.add(map);
            }
        }
        // Home page
        mHomeUrl = prefs.getString(KEY_HOME, mHomeUrl);
    }

    private void applySettings(SharedPreferences pref, String key) {
        // Produce null key while clear the pref on action 'Restore Default..."
        if (key == null) return;
        WebSettings settings = mWebView.getSettings();
        if (key.equals(getString(R.string.key_use_pc_agent)) ||
                key.equals(getString(R.string.key_pc_agent))) {
            if (pref.getBoolean(getString(R.string.key_use_pc_agent), false)) {
                settings.setUserAgentString(pref.getString(
                        getString(R.string.key_pc_agent), ""));
            } else {
                settings.setUserAgentString(
                        WebSettings.getDefaultUserAgent(this));
            }
        } else if (key.equals(getString(R.string.key_text_zoom))) {
            int zoom = pref.getInt(key, 0);
            if (zoom < 0) {
                zoom = (10 + zoom) * 10;
            } else {
                zoom = (1 + zoom) * 100;
            }
            settings.setTextZoom(zoom);
        } else if (key.equals(getString(R.string.key_java_script_enabled))) {
            settings.setJavaScriptEnabled(pref.getBoolean(key, true));
            updateSettingsState();
        } else if (key.equals(getString(R.string.key_block_network_image))) {
            settings.setBlockNetworkImage(pref.getBoolean(key, false));
            updateSettingsState();
        } else if (key.equals(getString(R.string.key_display_zoom_controls))) {
            settings.setDisplayZoomControls(pref.getBoolean(key, false));
        } else if (key.equals(getString(R.string.key_load_with_overview_mode))) {
            settings.setLoadWithOverviewMode(pref.getBoolean(key, false));
        } else if (key.equals(getString(R.string.key_media_playback_requires_user_gesture))) {
            settings.setMediaPlaybackRequiresUserGesture(pref.getBoolean(key, true));
        } else if (key.equals(getString(R.string.key_allow_loads_third_app))) {
            mWebView.setAllowCallOtherApp(pref.getBoolean(key, false));
        } else if (key.equals(getString(R.string.key_cache_mode))) {
            settings.setCacheMode(Integer.parseInt(pref.getString(key, "-1")));
        } else if (key.equals(getString(R.string.key_accept_third_party_cookies))) {
            CookieManager.getInstance().setAcceptThirdPartyCookies(
                    mWebView, pref.getBoolean(key, true));
        }
    }

    private void updateSettingsState() {
        mWebView.post(() -> {
            WebSettings settings = mWebView.getSettings();
            boolean flagImg = settings.getBlockNetworkImage();
            boolean flagJs = settings.getJavaScriptEnabled();
            if (isPortrait) {
                int resImg = flagImg ? R.drawable.ic_no_image_24 :
                        R.drawable.ic_image_24;
                mButtonImage.setBackgroundResource(resImg);
                mButtonImage.setEnabled(true);
                int resJs = flagJs ? R.drawable.ic_js :
                        R.drawable.ic_no_js;
                mButtonJS.setBackgroundResource(resJs);
                mButtonJS.setEnabled(true);
            } else {
                mMenuLoadImage.setEnabled(true);
                mMenuLoadImage.setChecked(!flagImg);
                mMenuJavascript.setEnabled(true);
                mMenuJavascript.setChecked(flagJs);
            }
        });
    }

    private void readSettings() {
        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
        for (String key : pref.getAll().keySet()) {
            applySettings(pref, key);
        }
    }

    /**
     * 显示消息
     *
     * @param msg 要显示的消息
     */
    private void showToast(String msg) {
        Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
    }

    private void updateBookmarkOrHistory(
            List<Map<String, String>> data, boolean isBookmarks) {
        data.clear();
        // bookmarks
        if (isBookmarks) {
            for (Map.Entry<String, String> entry : mBookmarks.entrySet()) {
                HashMap<String, String> map = new HashMap<>();
                map.put(FLAG_URL, entry.getKey());
                map.put(FLAG_TITLE, entry.getValue());
                data.add(map);
            }
            return;
        }
        // history
        WebBackForwardList bfList = mWebView.copyBackForwardList();
        int bfSize = bfList.getSize();
        int maxSize = 200;
        // The item 0 is the home page, do not show here.
        for (int i = bfSize - 1; i > Math.max(0, bfSize - maxSize); i--) {
            Map<String, String> map = new HashMap<>();
            map.put(FLAG_URL, bfList.getItemAtIndex(i).getUrl());
            map.put(FLAG_TITLE, bfList.getItemAtIndex(i).getTitle());
            data.add(map);
        }
        int hSize = mHistory.size();
        if (data.size() < maxSize && hSize > 0) {
            data.addAll(mHistory.subList(0, Math.min(maxSize - data.size(), hSize)));
        }
    }

    /**
     * 设置主页
     */
    private void setHomePage() {
        Util.showDialog(
                this,
                "Set Home Page",
                mHomeUrl == null ? "" : mHomeUrl,
                DialogType.EDIT,
                result -> {
                    if (!result.isEmpty()) mHomeUrl = result;
                }
        );
    }

    private void setBottomPanelVisibility(boolean visible, int delay) {
        if (mTimer != null) {
            mTimer.cancel();
            mTimer = null;
        }
        mTimer = new Timer();
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                runOnUiThread(() -> {
                    if (!isPortrait) return;
                    mFAB.setImageResource(visible ? R.drawable.ic_arrow_right_24
                            : R.drawable.ic_arrow_left_24);
                    int toX = visible ? 0 : ((View) mBottomControlPanel.getParent())
                            .getWidth();
                    mBottomControlPanel.clearAnimation();
                    mBottomControlPanel.animate()
                            .translationX(toX)
                            .setDuration(300)
                            .start();
                    if (visible) setBottomPanelVisibility(false, MAX_DELAY);
                });
            }
        }, delay);
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();
        if (id == R.id.btn_find_next || id == R.id.btn_find_prev) {
            String s = etFindText.getText().toString();
            if (s.equals(currentFindText)) {
                mWebView.findNext(id == R.id.btn_find_next);
            } else {
                currentFindText = s;
                mWebView.findAllAsync(currentFindText);
            }
        } else if (id == R.id.btn_find_cancel) {
            mWebView.clearMatches();
            tvFindState.setText("");
            etFindText.setText("");
            currentFindText = "";
            findViewById(R.id.layout_find).setVisibility(View.GONE);
        } else if (id == R.id.fab) {
            if (isPortrait) {
                setBottomPanelVisibility(mBottomControlPanel.getX() > 0, 0);
            } else {
                boolean flag = mToolbar.getVisibility() == View.VISIBLE;
                mToolbar.setVisibility(flag ? View.GONE : View.VISIBLE);
                mFAB.setImageResource(
                        flag ? R.drawable.ic_no_fullscreen_24 :
                                R.drawable.ic_fullscreen_24
                );
            }
        } else if (isPortrait) {
            // restart timer
            setBottomPanelVisibility(true, 0);
            if (id == R.id.btn_home) {
                mWebView.loadUrl(mHomeUrl);
            } else if (id == R.id.btn_back) {
                mWebView.goBack();
            } else if (id == R.id.btn_forward) {
                mWebView.goForward();
            } else if (id == R.id.btn_refresh) {
                if (isRefreshed) {
                    mWebView.reload();
                } else {
                    mWebView.stopLoading();
                }
            } else if (id == R.id.btn_javascript) {
                reverseWebViewSettings(SettingType.ENABLED_JS, v);
            } else if (id == R.id.btn_image) {
                reverseWebViewSettings(SettingType.BLOCK_NETWORK_IMAGE, v);
            } else if (id == R.id.btn_fullscreen) {
                boolean flag = mToolbar.getVisibility() == View.VISIBLE;
                mToolbar.setVisibility(flag ? View.GONE : View.VISIBLE);
                int res = flag ? R.drawable.ic_no_fullscreen_24 :
                        R.drawable.ic_fullscreen_24;
                mButtonFullscreen.setBackgroundResource(res);
            }
        }
    }

    private <T> void reverseWebViewSettings(SettingType type, T holder) {
        if (holder instanceof View) {
            ((View) holder).setEnabled(false);
        } else if (holder instanceof MenuItem) {
            ((MenuItem) holder).setEnabled(false);
        }
        WebSettings ws = mWebView.getSettings();
        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
        switch (type) {
            case ENABLED_JS:
                pref.edit().putBoolean(getString(R.string.key_java_script_enabled),
                        !ws.getJavaScriptEnabled()).apply();
                break;
            case BLOCK_NETWORK_IMAGE:
                pref.edit().putBoolean(getString(R.string.key_block_network_image),
                        !ws.getBlockNetworkImage()).apply();
                break;
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    private void inflateMenu() {
        int menuSrc = isPortrait ? R.menu.option : R.menu.option_land;
        if (mOptionMenu == null) return;
        mOptionMenu.clear();
        getMenuInflater().inflate(menuSrc, mOptionMenu);
        if (!isPortrait) {
            mMenuRefresh = mOptionMenu.findItem(R.id.menu_refresh);
            mMenuLoadImage = mOptionMenu.findItem(R.id.menu_load_image);
            mMenuJavascript = mOptionMenu.findItem(R.id.menu_javascript);
        }
        MenuItem searchItem = mOptionMenu.findItem(R.id.search);
        searchItem.setOnMenuItemClickListener(item -> {
            mToolbarEdit.setVisibility(View.VISIBLE);
            mToolbarEdit.setText(mWebView.getUrl());
            mToolbarEdit.requestFocus();
            return true;
        });
        mToolbarEdit.setOnTouchListener((v, event) -> {
            if (event.getAction() != MotionEvent.ACTION_UP) return false;
            if (event.getX() > mToolbarEdit.getWidth() -
                    mToolbarEdit.getCompoundPaddingEnd()) {
                if (mToolbarEdit.getText().toString().isEmpty()) {
                    mToolbarEdit.clearFocus();
                } else {
                    mToolbarEdit.setText("");
                }
                return true;
            }
            return false;
        });
        mToolbarEdit.setOnEditorActionListener((v, actionId, event) -> {
            if (actionId != EditorInfo.IME_ACTION_GO) return false;
            String url = mToolbarEdit.getText().toString().toLowerCase().trim();
            mToolbarEdit.clearFocus();
            if (url.isEmpty()) return true;
            url = url.contains(":") ? url : "https://" + url;
            mWebView.loadUrl(url);
            return true;
        });
        mToolbarEdit.setOnFocusChangeListener((v, hasFocus) -> {
            if (!hasFocus) {
                mToolbarEdit.setVisibility(View.GONE);
                InputMethodManager imm =
                        (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(
                        mToolbarEdit.getWindowToken(), 0);
            }
            searchItem.setVisible(!hasFocus);
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        boolean result = super.onCreateOptionsMenu(menu);
        mOptionMenu = menu;
        try {
            Method m = menu.getClass()
                    .getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE);
            m.setAccessible(true);
            m.invoke(menu, true);
        } catch (Exception ignore) {
        }
        inflateMenu();
        return result;
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        boolean superResult = super.onOptionsItemSelected(item);
        int id = item.getItemId();
        if (!isPortrait) {
            if (id == R.id.menu_home) {
                mWebView.loadUrl(mHomeUrl);
                return superResult;
            } else if (id == R.id.menu_back) {
                if (mWebView.canGoBack()) {
                    mWebView.goBack();
                }
                return superResult;
            } else if (id == R.id.menu_forward) {
                if (mWebView.canGoForward()) {
                    mWebView.goForward();
                }
                return superResult;
            } else if (id == R.id.menu_refresh) {
                if (isRefreshed) {
                    mWebView.reload();
                } else {
                    mWebView.stopLoading();
                }
                return superResult;
            } else if (id == R.id.menu_load_image) {
                reverseWebViewSettings(SettingType.BLOCK_NETWORK_IMAGE, item);
                return superResult;
            } else if (id == R.id.menu_javascript) {
                reverseWebViewSettings(SettingType.ENABLED_JS, item);
                return superResult;
            }
        }
        if (id == R.id.add_bookmark) {
            //以url为键，因为title可能会有重复
            mBookmarks.put(mWebView.getUrl(), mWebView.getTitle());
            showToast(getString(R.string.bookmark_added));
        } else if (id == R.id.bookmarks_history) {
            mDrawerLayout.openDrawer(GravityCompat.START);
        } else if (id == R.id.find_in_page) {
            boolean visible = layoutFind.getVisibility() == View.VISIBLE;
            int visibility = visible ? View.GONE : View.VISIBLE;
            layoutFind.setVisibility(visibility);
            if (visible) {
                mWebView.clearMatches();
            }
        } else if (id == R.id.scan_qr_code) {
            Util.Callback scanCallback = result -> {
                Intent intent;
                int which = Integer.parseInt(result);
                if (which == 0) {
                    intent = new Intent(this, ScanningActivity.class);
                    intent.putExtra(
                            EXTRA_REQUEST_CODE,
                            CODE_SCANNING
                    );
                } else {
                    intent = new Intent();
                    intent.setAction(Intent.ACTION_GET_CONTENT);
                    intent.setType("image/*");
                    intent.putExtra(
                            EXTRA_REQUEST_CODE,
                            CODE_SCAN_LOCAL_PICTURE
                    );
                }
                mActivityLauncher.launch(intent);
            };
            Util.showDialog(
                    this,
                    getString(R.string.select_processor),
                    getString(R.string.scan_with_camera) + "," +
                            getString(R.string.scan_local_picture),
                    DialogType.SELECT,
                    scanCallback
            );
        } else if (id == R.id.edit_home_homepage) {
            setHomePage();
        } else if (id == R.id.show_download_manager) {
            Intent it = new Intent();
            it.setAction(DownloadManager.ACTION_VIEW_DOWNLOADS);
            startActivity(it);
        } else if (id == R.id.clear_cache) {
            mWebView.clearCache(true);
            showToast("Web Browser cache cleared.");
        } else if (id == R.id.menu_settings) {
            getSupportFragmentManager().beginTransaction()
                    .replace(R.id.drawer_layout, new SettingsFragment())
                    .commit();
        } else if (id == R.id.menu_save_page) {
            String savePath = Environment.getExternalStoragePublicDirectory(
                    Environment.DIRECTORY_DOWNLOADS
            ).getAbsolutePath() + "/";
            mWebView.saveWebArchive(
                    savePath,
                    true,
                    val -> Util.showDialog(
                            this,
                            val == null ? "Save Page Error" : "Page Saved",
                            val,
                            DialogType.INFO,
                            null
                    )
            );
        } else if (id == R.id.menu_about) {
            String webViewVersion;
            PackageInfo info = WebViewCompat.getCurrentWebViewPackage(this);
            webViewVersion = info == null ? "unknown" : info.versionName;
            String pkgVersion = "unknown";
            try {
                PackageInfo pkgInfo;
                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
                    pkgInfo = getPackageManager().getPackageInfo(
                            getPackageName(), PackageManager.PackageInfoFlags.of(0)
                    );
                } else {
                    //noLintDeprecation
                    pkgInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
                }
                pkgVersion = pkgInfo.versionName;
            } catch (PackageManager.NameNotFoundException ignore) {
            }
            Util.showDialog(
                    this,
                    getString(R.string.about),
                    getString(R.string.about_content, pkgVersion, webViewVersion),
                    DialogType.INFO,
                    null
            );
        } else if (id == R.id.menu_exit) {
            finish();
        }
        return superResult;
    }

    /**
     * 设备返回键事件处理
     */
    @Override
    public void onBackPressed() {
        FragmentManager fragmentManager = getSupportFragmentManager();
        SettingsFragment fragment = (SettingsFragment) fragmentManager
                .findFragmentById(R.id.drawer_layout);
        if (fragment != null) {
            fragmentManager.beginTransaction().remove(fragment).commit();
            return;
        }
        if (mWebView.canGoBack()) {
            mWebView.goBack();
            return;
        }
        long currentTime = System.currentTimeMillis();
        if (currentTime - mLastBackPressedTime > 3000) {
            mLastBackPressedTime = currentTime;
            showToast(getString(R.string.press_back_again));
            return;
        }
        super.onBackPressed();
    }
}
